/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "UpgradeDlg.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QProcess>
#include <QSettings>
#include "util.h"
#include "BurnToolCommon/Global.h"
#include "../platform/Securec/securec.h"

using namespace UtilClass;
using namespace LibMainClass;
using namespace UpgradeDlgClass;
namespace UpgradeDlgClass {
    const unsigned long FILE_VERSION_UNDEF = 0;
    const unsigned long FILE_VERSION_TRX = 1;
    const unsigned long FILE_VERSION_BZ2 = 2;
    const unsigned long FILE_VERSION_BIN = 3;
    const QString QMESSAGE_TITLE = "Upgrade";
    const int COLUMN_WIDTH_SIZE_20 = 20;
    const int COLUMN_WIDTH_SIZE_100 = 100;
    const int COLUMN_WIDTH_SIZE_240 = 240;
    const int COLUMN_MINIMUM_HEIGHT_SIZE_192 = 192;
    const int QAPP_EXIT_VALUE = 773;
    const int TEMP_PATH_LEN = 256;
    const int PROGRESS_MAX = 100;
    const int HEXADECIMAL = 16;
    const int EXECUTE_INTERVAL = 10;
    const int SET_LANGUAGE_SLEEP_TIME = 500;
}

UpgradeDlg::UpgradeDlg(QWidget *parent)
    : QMainWindow(parent),
      ui(new Ui::UpgradeDlg),
      policyVersion(MUPG_POLICY_UNKNOWN),
      strExt(""),
      aboutWin(nullptr),
      actAbout(nullptr),
      actChangeChip(nullptr),
      binModel(new BurnDlg::BinFileModel()),
      changeChip(nullptr),
      actEnglish(new QAction("English")),
      actChinese(new QAction("中文")),
      menuWifiLanguage(new QMenu(stringClass.GetActionLanguage()))
{
    actAbout = new QAction(stringClass.GetAbout());
    actChangeChip = new QAction(stringClass.GetChangeChip());
    if (ui == nullptr || actAbout == nullptr || actChangeChip == nullptr) {
        return;
    }
    ui->setupUi(this);
    TspMutGetConfigInfo();
    SlotCbnSetfocusComboCom();
    char strTempPath[TEMP_PATH_LEN];
    GetTempPathA(TEMP_PATH_LEN, strTempPath);
    tempPath = QString::fromUtf8(strTempPath, sizeof(strTempPath));
    InitConnect();
    InitTableView();
    ui->progressBarReceive->setRange(0, PROGRESS_MAX);
    ui->progressBarReceive->setValue(0);
    LibMain::ulUpgPolicy = MUPG_POLICY_MODEM;
    ui->btnStop->setEnabled(false);
    ui->btnStart->setEnabled(false);
    ui->menuHelp->addAction(actAbout);
    ui->menuOption->addAction(actChangeChip);
    QSettings cfg(Global::GetAppConfigPath() + "\\config_all_chip_type.ini", QSettings::IniFormat);
    int translate = cfg.value("AHHCHIPTYPECONFIG/TRANSLATE").toInt();
    if (translate == 0) {
        actEnglish->setCheckable(true);
        actEnglish->setChecked(true);
        actEnglish->setEnabled(true);
        actChinese->setCheckable(false);
        actChinese->setChecked(false);
        actChinese->setEnabled(true);
    } else {
        actEnglish->setCheckable(false);
        actEnglish->setChecked(false);
        actEnglish->setEnabled(true);
        actChinese->setCheckable(true);
        actChinese->setChecked(true);
        actChinese->setEnabled(true);
    }
    if (menuWifiLanguage != nullptr) {
        menuWifiLanguage->addAction(actEnglish);
        menuWifiLanguage->addAction(actChinese);
    }
    connect(actEnglish, &QAction::triggered, this, &UpgradeDlg::SlotSetEnglish);
    connect(actChinese, &QAction::triggered, this, &UpgradeDlg::SlotSetChinese);
    ui->menuSetting->addMenu(menuWifiLanguage);
    fileTypeMap.insert("trx", FILE_VERSION_TRX);
    fileTypeMap.insert("bz2", FILE_VERSION_BZ2);
    fileTypeMap.insert("bin", FILE_VERSION_BIN);
}

UpgradeDlg::~UpgradeDlg()
{
    if (ui != nullptr) {
        delete ui;
        ui = nullptr;
    }
    if (actAbout != nullptr) {
        delete actAbout;
        actAbout = nullptr;
    }
    if (actChangeChip != nullptr) {
        delete actChangeChip;
        actChangeChip = nullptr;
    }
    if (aboutWin != nullptr) {
        delete aboutWin;
        aboutWin = nullptr;
    }
    if (binModel != nullptr) {
        delete binModel;
        binModel = nullptr;
    }
    if (changeChip != nullptr) {
        delete changeChip;
        changeChip = nullptr;
    }
}

void UpgradeDlg::InitConnect()
{
    // setting
    if (actChangeChip != nullptr) {
        connect(actChangeChip, &QAction::triggered, this, &UpgradeDlg::SlotShowChangeChip);
    }
    // help
    if (actAbout != nullptr) {
        connect(actAbout, &QAction::triggered, this, &UpgradeDlg::SlotShowAbout);
    }
    if (ui != nullptr) {
        // button
        connect(ui->btnSelectFile, &QPushButton::clicked, this, &UpgradeDlg::SlotBnClickedButtonSelectfile);
        connect(ui->btnStart, &QPushButton::clicked, this,  &UpgradeDlg::SlotBtnStart);
        connect(ui->btnStop, &QPushButton::clicked, this,  &UpgradeDlg::SlotBtnStop);
        // tableView
        connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked, this,
                &UpgradeDlg::SlotBnClickedTableHeader);
        // comboBox
        connect(ui->cbNetworkCard, &BurnDlg::ComboBoxCom::Clicked, this, &UpgradeDlg::SlotCbnSetfocusComboCom);
    }
    // timer
    connect(&startTimer, &QTimer::timeout, this, &UpgradeDlg::SlotStartTimerOut);
}

void UpgradeDlg::SlotCbnSetfocusComboCom()
{
    LibMain::MutGetAllNetworkCard();
    unsigned long ulNetCard = 0;
    if (ui == nullptr) {
        return;
    }
    ui->cbNetworkCard->clear();
    while (ulNetCard < MAX_NETCARD_NUM && LibMain::netcardList[ulNetCard][0] != '\0') {
        ui->cbNetworkCard->addItem(QString::fromLatin1(LibMain::netcardList[ulNetCard]));
        ulNetCard++;
    }
}


void UpgradeDlg::SlotBnClickedButtonSelectfile()
{
    QString filePath = QFileDialog::getOpenFileName(this, tr("Select File"), "", stringClass.GetIdsString32941());
    if (!filePath.isEmpty()) {
        AnalyseBinFile(filePath);
    }
}

void UpgradeDlg::AnalyseBinFile(QString filePath)
{
    if (ui == nullptr || binModel == nullptr) {
        return;
    }
    if (TRUE == LibMain::bIsSendingPack) {
        QMessageBox::critical(this, stringClass.GetMessageError(),
                              tr("Stop file sending before any other operation."), stringClass.GetYes());
        return;
    }
    if (memset_s(LibMain::pszImgFileName, MAX_FILE_PATH + 1, 0, MAX_FILE_PATH + 1) != 0) {
        return;
    }
    ui->lineEditSeleteFile->setText(filePath);
    if (strncpy_s(LibMain::pszImgFileName, MAX_FILE_PATH + 1, filePath.toUtf8(), filePath.toUtf8().size()) != 0) {
        return;
    }
    strExt = QFileInfo(filePath).suffix();
    TspMutGetIniFileName(filePath.toUtf8());
    MutParseIniFile(LibMain::pszIniFileName);
    QMap<int, BIN_INFO> mapBinInfo;
    QMap<int, bool> mapCheck;
    for (int i = 0; i < PATITION_NUM_MAX; i++) {
        QString str = QString::fromUtf8(reinterpret_cast<char *>(LibMain::stPatitions[i].partitionName));
        if (str.isEmpty()) {
            continue;
        }
        BIN_INFO info;
        info.path = filePath;
        info.fileName = str;
        info.fileSize = "0";
        info.fileIndex = "0";
        info.burnAddr = "0x" + QString::number(LibMain::stPatitions[i].addrStart, HEXADECIMAL);
        info.burnSize = "0x" + QString::number(
            LibMain::stPatitions[i].addrEnd - LibMain::stPatitions[i].addrStart, HEXADECIMAL);
        mapBinInfo[mapBinInfo.size()] = info;
        if (LibMain::stPatitions[i].cover == PATITION_CHECK) {
            mapCheck[i] = true;
        } else {
            mapCheck[i] = false;
        }
    }
    binModel->FetchData(mapCheck);
    binModel->FetchData(mapBinInfo);
    ui->btnStart->setEnabled(false);
    ui->btnSelectFile->setEnabled(false);
    CheckVersion();
}

DWORD UpgradeDlg::CheckVersion()
{
    if (ui == nullptr) {
        return 0;
    }
    QString strVersionRet;
    ResetState();
    DWORD fileType = GetFileType();
    QString cleanPath = QDir::cleanPath(LibMain::pszImgFileName);
    if (cleanPath.isEmpty()) {
        return false;
    }
    QString fileName = cleanPath;
    if (fileType == FILE_VERSION_UNDEF) {
        QMessageBox::warning(this, QMESSAGE_TITLE, tr("File Version: Error file type."));
        ui->btnSelectFile->setEnabled(true);
        return 1;
    } else if (fileType == FILE_VERSION_BIN) {
        strVersionRet = "";
        policyVersion = MUPG_POLICY_MODEM;
    }

    if (strVersionRet == "Error") {
        QMessageBox::warning(this, QMESSAGE_TITLE, tr("Parsing file version failed. Check the file."));
    } else if (strVersionRet == "Size") {
        QMessageBox::warning(this, QMESSAGE_TITLE, tr("File size error."));
    }

    ui->btnStart->setEnabled(true);
    ui->btnSelectFile->setEnabled(true);
    return 1;
}

void UpgradeDlg::ResetState()
{
    policyVersion = MUPG_POLICY_UNKNOWN;
}

DWORD UpgradeDlg::GetFileType()
{
    return fileTypeMap[strExt];
}

bool UpgradeDlg::CheckImgFile(QString imgFileName)
{
    QFile imgFile(imgFileName);
    if (!imgFile.open(QIODevice::ReadOnly)) {
        QMessageBox::warning(this, QMESSAGE_TITLE, tr("Make sure the file exists and is readable."));
        ui->lineEditSeleteFile->clear();
        return false;
    }
    imgFile.close();

    if (LibMain::ulUpgPolicy != policyVersion || MutParsePkghead(imgFileName) != MUT_OK) {
        QMessageBox::warning(this, QMESSAGE_TITLE, tr("Parsing file failed,"
                                                   " maybe caused by unmatched upgrade policy."));
        return false;
    }
    // Add your control notification handler code here
    // 置线程运行标志位
    LibMain::ulStopFlag = UPG_THREAD_RUN;

    // 测试打开文件
    if (!imgFile.open(QIODevice::ReadOnly)) {
        QMessageBox::warning(this, QMESSAGE_TITLE, tr("Make sure the file exists and is readable."));
        ui->lineEditSeleteFile->clear();
        LibMain::ulPacketNo = 0;
        LibMain::ulSendImgFileNum = 0;
        ui->progressBarReceive->setValue(0);
        ui->lineEditSendTime->clear();
        ui->lineEditCurrentPacketNo->clear();
        return false;
    }
    imgFile.close();
    return true;
}

void UpgradeDlg::SlotBtnStart()
{
    if (ui == nullptr || binModel == nullptr) {
        return;
    }
    QMap<int, BIN_INFO> info = binModel->GetBinFileInfoData();
    for (auto it = info.begin(); it != info.end(); it++) {
        for (int i = 0; i < PATITION_NUM_MAX; i++) {
            QString str = QString::fromUtf8(reinterpret_cast<char *>(LibMain::stPatitions[i].partitionName));
            if (str.isEmpty() || str != it.value().fileName) {
                continue;
            }
            LibMain::stPatitions[i].cover = binModel->GetCheck(it.key()) ? PATITION_CHECK : PATITION_NOT_CHECK;
        }
    }
    LibMain::ulPacketNo = 0;
    LibMain::ulSendImgFileNum = 0;
    ui->progressBarReceive->setValue(0);
    ui->lineEditSendTime->clear();
    ui->lineEditCurrentPacketNo->clear();
    QString cleanPath = QDir::cleanPath(LibMain::pszImgFileName);
    if (cleanPath.isEmpty()) {
        return;
    }
    if (!CheckImgFile(cleanPath)) {
        return;
    }
    if (TRUE == LibMain::bIsSendingPack) {
        LibMain::bIsSendingPack = FALSE;
        return;
    }
    DWORD lpThreadId;
    startTimer.start(EXECUTE_INTERVAL);
    // 创建一个线程
    CreateThread(nullptr, 0, LibMain::TspMutMulticast, reinterpret_cast<LPVOID>(ui->cbNetworkCard->currentIndex()),
        0, &lpThreadId);
    if (ui->btnStart->isEnabled()) {
        ui->btnStart->setEnabled(false);
        ui->btnSelectFile->setEnabled(false);
        ui->cbNetworkCard->setEnabled(false);
        ui->btnStop->setEnabled(true);
    }
    return;
}

unsigned long UpgradeDlg::MutParsePkghead(const QString &pszImgFileName)
{
    if (LibMain::ulUpgPolicy == MUPG_POLICY_HGW || LibMain::ulUpgPolicy == MUPG_POLICY_CPE
        || LibMain::ulUpgPolicy == MUPG_POLICY_MINISYS) {
        return MUT_OK;
    } else if (LibMain::ulUpgPolicy == MUPG_POLICY_MODEM) {
        return ParseBinHeader(pszImgFileName);
    } else {
        return TSP_D_ERR_MUT_OPEN_FILE_ERR;
    }
    return MUT_OK;
}

unsigned long UpgradeDlg::ParseBinHeader(const QString &pszImgFileName)
{
    QFile file(pszImgFileName);
    if (file.open(QIODevice::ReadOnly)) {
        LibMain::stMultiHGWHead.ulMagicNumber = 0xB0A555AA;
        LibMain::stMultiHGWHead.ucPackageType = MUPG_PACKAGE_TYPE_MODEM;
        LibMain::stMultiHGWHead.ulImageSize = file.size();
        LibMain::stMultiHGWHead.ulTotalPackageNum = (LibMain::stMultiHGWHead.ulImageSize % ONE_SEGMENT_BUFF_LEN > 0) ?
                                             (LibMain::stMultiHGWHead.ulImageSize / ONE_SEGMENT_BUFF_LEN + 1) :
                                             (LibMain::stMultiHGWHead.ulImageSize / ONE_SEGMENT_BUFF_LEN);
        LibMain::stMultiHGWHead.ulPackageSize = ONE_SEGMENT_BUFF_LEN;
        file.close();
        return MUT_OK;
    }

    return TSP_D_ERR_MUT_OPEN_FILE_ERR;
}

void UpgradeDlg::SlotStartTimerOut()
{
    if (ui == nullptr) {
        return;
    }
    unsigned int progressPos = 0;
    // Add your message handler code here and/or call default
    ui->lineEditSendTime->setText(QString::number(LibMain::ulSendImgFileNum));
    ui->lineEditCurrentPacketNo->setText(QString::number(LibMain::ulPacketNo));
    /* 设置进度条 */
    if (LibMain::ulImgFileLen != 0) {
        if (LibMain::ulImgFileLen <= ONE_SEGMENT_BUFF_LEN) {
            progressPos = PROGRESS_MAX;
        } else {
            progressPos = PROGRESS_MAX * (LibMain::ulPacketNo * ONE_SEGMENT_BUFF_LEN) / LibMain::ulImgFileLen;
        }
        ui->progressBarReceive->setValue(progressPos);
    }
    if (LibMain::ulRetCode != MUT_OK) {
        if (LibMain::ulRetCode == TSP_D_ERR_MUT_NETCARD_IP_ERR) {
            LibMain::ulRetCode = MUT_OK;
            QMessageBox::warning(this, QMESSAGE_TITLE, tr("The IP address of network card is incorrect."));
        } else if (LibMain::ulRetCode == TSP_D_ERR_MUT_SOCKET_ERR) {
            LibMain::ulRetCode = MUT_OK;
            QMessageBox::warning(this, QMESSAGE_TITLE, tr("Socket error."));
        } else if (LibMain::ulRetCode == TSP_D_ERR_MUT_OPEN_FILE_ERR) {
            LibMain::ulRetCode = MUT_OK;
            QMessageBox::warning(this, QMESSAGE_TITLE, tr("Failed to open package file."));
        } else if (LibMain::ulRetCode == TSP_D_ERR_MUT_INTERNAL_ERR) {
            LibMain::ulRetCode = MUT_OK;
            QMessageBox::warning(this, QMESSAGE_TITLE, tr("Internal error."));
        } else if (LibMain::ulRetCode == TSP_D_ERR_MUT_SEND_PACKET_ERR) {
            LibMain::ulRetCode = MUT_OK;
            QMessageBox::warning(this, QMESSAGE_TITLE, tr("File sending failed. "
                                                       "Check network connection."));
        } else if (LibMain::ulRetCode == TSP_D_ERR_MUT_CRC_CMP_NOK) {
            LibMain::ulRetCode = MUT_OK;
            QMessageBox::warning(this, QMESSAGE_TITLE, tr("CRC error!"));
        }
        if (LibMain::ulRetCode == UPG_D_ERR_MUT_NETWORK_ERR) {
            ui->btnStop->setEnabled(true);
            ui->btnStart->setEnabled(false);
        } else {
            ui->btnStop->setEnabled(false);
            ui->btnStart->setEnabled(true);
            ui->btnSelectFile->setEnabled(true);
            ui->cbNetworkCard->setEnabled(true);
        }
    }
}

void UpgradeDlg::SlotBtnStop()
{
    LibMain::bIsSendingPack = FALSE;
    /* 设标志位停止发包线程运行 */
    LibMain::ulStopFlag = UPG_THREAD_STOP;
    ui->btnStop->setEnabled(false);
    ui->btnStart->setEnabled(true);
    ui->btnSelectFile->setEnabled(true);
    ui->cbNetworkCard->setEnabled(true);
    startTimer.stop();
    return;
}

void UpgradeDlg::SlotShowAbout()
{
    if (aboutWin == nullptr) {
        aboutWin = new BurnDlg::AboutWindow();
    }
    if (aboutWin != nullptr) {
        aboutWin->show();
    }
}

void UpgradeDlg::InitTableView()
{
    if (ui == nullptr || binModel == nullptr) {
        return;
    }
    binModel->SetEditableCheckbox(true);
    ui->tableView->setModel(binModel);
    ui->tableView->setDragEnabled(true);
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableView->setDefaultDropAction(Qt::MoveAction);
    ui->tableView->setDragDropMode(QAbstractItemView::InternalMove);
    ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_CHECK, COLUMN_WIDTH_SIZE_20);
    ui->tableView->setColumnHidden(TABLEVIEW_COLUMN_ADD, true);
    ui->tableView->setColumnHidden(TABLEVIEW_COLUMN_DELETE, true);
    ui->tableView->setColumnHidden(TABLEVIEW_COLUMN_FILE_INDEX, true);
    ui->tableView->setColumnHidden(TABLEVIEW_COLUMN_FILE_SIZE, true);
    ui->tableView->setColumnHidden(TABLEVIEW_COLUMN_TYPE, true);
    ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_NAME, COLUMN_WIDTH_SIZE_100);
    ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_PAHT, COLUMN_WIDTH_SIZE_240);
    ui->tableView->setMinimumHeight(COLUMN_MINIMUM_HEIGHT_SIZE_192);
}

void UpgradeDlg::SlotBnClickedTableHeader(int logicalIndex)
{
    if (binModel == nullptr) {
        return;
    }
    if (logicalIndex == 0) {
        QMap<int, bool> mapCheck = binModel->GetMapCheck();
        int i = 0;
        for (; i < mapCheck.size(); i++) {
            if (!binModel->GetCheck(i)) {
                break;
            }
        }
        if (i == mapCheck.size()) {
            for (int j = 0; j < mapCheck.size(); j++) {
                mapCheck[j] = false;
            }
        } else {
            for (int j = 0; j < mapCheck.size(); j++) {
                mapCheck[j] = true;
            }
        }
        binModel->FetchData(mapCheck);
    }
}

void UpgradeDlg::SlotShowChangeChip()
{
    changeChip = new BurnDlg::ChangeChip(this);
    if (changeChip != nullptr) {
        connect(changeChip, &BurnDlg::ChangeChip::SignalChanceChipVersion, this, &UpgradeDlg::SlotChanceChipVersion);
        changeChip->show();
    }
}

void UpgradeDlg::SlotChanceChipVersion(const QString &version)
{
    Global::currentChipTypeId = Global::ChipTypeStringToId(version);
    Global::SaveCfg();
    Global::CloseApp(QAPP_EXIT_VALUE);
}

void UpgradeDlg::SlotSetEnglish()
{
    QSettings cfg(Global::GetAppConfigPath() + "\\config_all_chip_type.ini", QSettings::IniFormat);
    cfg.setValue("AHHCHIPTYPECONFIG/TRANSLATE", 0);
    Sleep(SET_LANGUAGE_SLEEP_TIME);
    Global::CloseApp(QAPP_EXIT_VALUE);
}

void UpgradeDlg::SlotSetChinese()
{
    QSettings cfg(Global::GetAppConfigPath() + "\\config_all_chip_type.ini", QSettings::IniFormat);
    cfg.setValue("AHHCHIPTYPECONFIG/TRANSLATE", 1);
    Sleep(SET_LANGUAGE_SLEEP_TIME);
    Global::CloseApp(QAPP_EXIT_VALUE);
}

